package tech.summerly.weather.data.local

import android.arch.lifecycle.LiveData
import android.arch.persistence.room.*
import tech.summerly.weather.model.UserRegion
import tech.summerly.weather.model.UserRegion.Companion.ID_NONE
import tech.summerly.weather.util.LoggerLevel
import tech.summerly.weather.util.log

@Dao
abstract class UserRegionDao(val database: RegionDataBase) {

    @Insert(onConflict = OnConflictStrategy.FAIL)
    protected abstract fun insertRegionInternal(region: UserRegion): Long

    @Delete
    protected abstract fun removeRegionInternal(region: UserRegion)

    @Query("select * from user_region")
    abstract fun getTotalUserRegions(): LiveData<List<UserRegion>>

    @Query("select * from user_region where id = :id")
    abstract fun getUserRegion(id: Long): UserRegion?

    @Query("select * from user_region where previous = :id")
    protected abstract fun getRegionWhichPreviousIs(id: Long): UserRegion?

    @Query("select * from user_region where next = :id")
    protected abstract fun getRegionWhichNextIs(id: Long): UserRegion?

    @Update
    protected abstract fun updateRegion(userRegion: UserRegion)


    /**
     * 改变[userRegion]在表中的顺序位置
     *
     * @param previous 前item的id
     * @param next 后item的id
     */
    fun moveUserRegion(userRegion: UserRegion, previous: Long, next: Long) {
        removeUserRegion(userRegion)
        insertUserRegion(userRegion.copy(previous = previous, next = next))
    }

    /**
     * 从表中移除指定的[userRegion]
     */
    fun removeUserRegion(userRegion: UserRegion) {
        val previous = getUserRegion(userRegion.previous)
        val next = getUserRegion(userRegion.next)
        removeRegionInternal(userRegion)
        previous?.let { updateRegion(it.copy(next = next?.id ?: ID_NONE)) }
        next?.let { updateRegion(it.copy(previous = previous?.id ?: ID_NONE)) }
    }

    fun insertUserRegion(userRegion: UserRegion): Long {
        return when (ID_NONE) {
            userRegion.next -> insertToTail(userRegion)
            userRegion.previous -> insertToHead(userRegion)
            else -> insertToCenter(userRegion)
        }
    }

    private fun insertToCenter(userRegion: UserRegion): Long {
        log { "userRegion : $userRegion" }
        val previous = getUserRegion(userRegion.previous)!!
        val next = getUserRegion(userRegion.next)!!

        if (previous.next != next.id || next.previous != previous.id) {
            log(LoggerLevel.ERROR) { "error insert previous: $previous , next: $next" }
        }

        val id = insertRegionInternal(userRegion)
        updateRegion(previous.copy(next = id))
        updateRegion(next.copy(previous = id))
        return id
    }

    private fun insertToHead(userRegion: UserRegion): Long {
        val header = getRegionWhichPreviousIs(ID_NONE) ?: return insertToEmptyTable(userRegion)
        val id = insertRegionInternal(userRegion.copy(next = header.id))
        updateRegion(header.copy(previous = id))
        return id
    }

    private fun insertToTail(userRegion: UserRegion): Long {
        val tail = getRegionWhichNextIs(ID_NONE) ?: return insertToEmptyTable(userRegion)
        val id = insertRegionInternal(userRegion.copy(previous = tail.id))
        updateRegion(tail.copy(next = id))
        return id
    }

    private fun insertToEmptyTable(userRegion: UserRegion): Long {
        return insertRegionInternal(userRegion.copy(previous = ID_NONE, next = ID_NONE))
    }
}